package org.opens.mybatisplus.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.base.Preconditions;
import com.google.gson.Gson;
import org.opens.mybatisplus.dao.MyBatisPersonDao;
import org.opens.mybatisplus.pojo.dto.MyBatisPerson;
import org.opens.mybatisplus.service.MybatisPersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service(value = "mybatisPersonService")
public class MybatisPersonServiceImpl implements MybatisPersonService {

    @Autowired
    private MyBatisPersonDao myBatisPersonDao;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public List<MyBatisPerson> findByIds(List<Integer> ids) {
        if(ids == null || ids.size() == 0) {
            return new ArrayList<>();
        }
        return Optional.ofNullable(myBatisPersonDao.findByIds(ids)).orElse(new ArrayList<>());
    }

    @Override
    public MyBatisPerson findById(Integer id) throws Exception {
        return Optional.ofNullable(myBatisPersonDao.findById(id)).orElseThrow(() -> new Exception("对应的数据不存在"));
    }

    @Override
    public Optional<MyBatisPerson> findByIdUseRedisCache(Integer id) {
        Gson gson = new Gson();
        String s = redisTemplate.opsForValue().get("findByIdUseRedisCache:" + id);
        if(StrUtil.isNotBlank(s)) {
            MyBatisPerson person = gson.fromJson(s, MyBatisPerson.class);
            return Optional.ofNullable(person);
        }
        MyBatisPerson personDB = myBatisPersonDao.findById(id);
        redisTemplate.opsForValue().set("findByIdUseRedisCache:" + id, gson.toJson(personDB));
        return Optional.ofNullable(personDB);
    }

    @Override
    public Optional<MyBatisPerson.Count> countAll() {
        MyBatisPerson.Count count1 = myBatisPersonDao.countAll();
        return Optional.of(count1);
    }

    @Override
    public MyBatisPerson.Count2 countAll2() {
        return Optional.ofNullable(myBatisPersonDao.countAll2())
                .orElse(MyBatisPerson.Count2.getInstance());
    }

    @Override
    public <T> Map<T, MyBatisPerson> findAll_useFunctionToMap(Function<MyBatisPerson, T> function) {
        List<MyBatisPerson> all = myBatisPersonDao.findAll();
        return all.stream().collect(Collectors.toMap(function, x -> x));
    }

    @Override
    public <K, V> Map<K, V> findAll_useFunctionToMap(Function<MyBatisPerson, K> keyFunctioon, Function<MyBatisPerson, V> valueFunctioon) {
        List<MyBatisPerson> all = myBatisPersonDao.findAll();
        return all.stream().collect(Collectors.toMap(keyFunctioon, valueFunctioon));
    }

    /**
     * 说明:
     *      1. Collectors.toMap()的第三个参数(oldValue, newValue) -> newValue是定义的当有key重复时应该使用新值或者旧值.
     *          - 如果没有这个参数, 不出现id重复这个方法还可以用, 但是一旦出现id重复就会导致报错.
     *
     */
    @Override
    public <K, V> Map<K, V> translateToMap(List<MyBatisPerson> list, Function<MyBatisPerson, K> keyFunctioon, Function<MyBatisPerson, V> valueFunctioon) {
        Preconditions.checkNotNull(keyFunctioon, "转换key的函数不能为null");
        Preconditions.checkNotNull(valueFunctioon, "转换key的函数不能为null");

        if(CollectionUtil.isEmpty(list)) {
            return new HashMap<>();
        }

        return list.stream().collect(Collectors.toMap(keyFunctioon, valueFunctioon, (oldValue, newValue) -> newValue));
    }

    @Override
    public Map<Integer, MyBatisPerson> findMap_useIdIsKey_byIds(List<Integer> ids) {
        return translateToMap(myBatisPersonDao.findByIds(ids), MyBatisPerson::getId, x -> x);
    }

    @Override
    public Integer insertAndReturnId(MyBatisPerson myBatisPerson) {
        return myBatisPersonDao.insertAndReturnId(myBatisPerson);
    }

    @Override
    public void batchUpdate(List<MyBatisPerson> personList) {
        myBatisPersonDao.batchUpdate(personList);
    }

    @Override
    public void batchUpdate2(List<MyBatisPerson> personList) {
        myBatisPersonDao.batchUpdate2(personList);
    }

    @Override
    public List<MyBatisPerson> useTrimQuery(MyBatisPerson myBatisPerson) {
        myBatisPerson.setNameValue(StrUtil.addSuffixIfNot(myBatisPerson.getNameValue(), "%"));
        return myBatisPersonDao.useTrimQuery(myBatisPerson);
    }

    @Override
    public List<MyBatisPerson> useBindQuery(MyBatisPerson myBatisPerson) {
        return myBatisPersonDao.useBindQuery(myBatisPerson);
    }

    @Override
    public void deleteById(Integer id) throws Exception {
        myBatisPersonDao.deleteById(Objects.requireNonNull(id));
    }

    @Override
    public void update(MyBatisPerson person) {
        if ( person == null || person.getId() == null ) {
            throw new NullPointerException();
        }
        myBatisPersonDao.update(person);
    }

    @Override
    public IPage<MyBatisPerson> pageOfCondition(Integer pageNumber, Integer pageSize, MyBatisPerson person) {
        Page<MyBatisPerson> page = new Page<>(pageNumber, pageSize);
        return myBatisPersonDao.pageOfConditions(page, person);
    }
}
